using System;
using System.Collections;
using System.Drawing;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using ActiproSoftware.SyntaxEditor.Addons.Simple.Ast;

namespace ActiproSoftware.SyntaxEditor.Addons.Simple {

	/// <summary>
	/// Represents a <c>Simple</c> language definition.
	/// </summary>
	public class SimpleSyntaxLanguage : MergableSyntaxLanguage, ISemanticParserServiceProcessor {

		private SimpleLexicalParser		lexicalParser							= new SimpleLexicalParser(true);

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		// OBJECT
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Initializes a new instance of the <c>SimpleSyntaxLanguage</c> class.
		/// </summary>
		public SimpleSyntaxLanguage() : base("Simple") {
			this.ExampleText = @"/*
	The SIMPLE language example demonstrates a C-like
	language that only knows several keywords
	and operators.  All variables are integer numbers.
	See the grammar definition for EBNF notation
	of the semantic grammar.
	
	This sample is intended to provide a simple base 
	for implementing your own advanced language 
	add-on.  It even demonstrates a high-level 
	implementation of IntelliPrompt for function
	names, parameters, and keywords.
*/

function Add(x, y) {
	return x + y;
}

function Increment(x) {
	return (x + 1);
}

function IncrementAndMultiply(x, y) {
	// This function calls another function
	var result;
	result = Increment(x);
	return result * y;
}
";

			// Initialize highlighting styles
			this.HighlightingStyles.Add(new HighlightingStyle("KeywordStyle", null, Color.Blue, Color.Empty));
			this.HighlightingStyles.Add(new HighlightingStyle("CommentStyle", null, Color.Green, Color.Empty));
			this.HighlightingStyles.Add(new HighlightingStyle("NumberStyle", null, Color.Purple, Color.Empty));

			// Initialize lexical states
			this.LexicalStates.Add(new DefaultLexicalState(SimpleLexicalStateID.Default, "DefaultState"));
			this.DefaultLexicalState = this.LexicalStates["DefaultState"];
			this.LexicalStates["DefaultState"].DefaultHighlightingStyle = this.HighlightingStyles["DefaultStyle"];
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////
		// INTERFACE IMPLEMENTATION
		/////////////////////////////////////////////////////////////////////////////////////////////////////
		
		/// <summary>
		/// Performs a semantic parsing operation using the data in the <see cref="SemanticParserServiceRequest"/>.
		/// </summary>
		/// <param name="request">A <see cref="SemanticParserServiceRequest"/> containing data about what to parse.</param>
		void ISemanticParserServiceProcessor.Process(SemanticParserServiceRequest request) {
			request.SemanticParseData = MergableLexicalParserManager.PerformSemanticParse(this, request.TextBufferReader, request.Filename) as ISemanticParseData;
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////
		// PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////
	
		/// <summary>
		/// Returns the quick info for the <see cref="SyntaxEditor"/> at the specified offset.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> to examine.</param>
		/// <param name="offset">The offset to examine.  The offset is updated to the start of the context.</param>
		/// <returns>The quick info for the <see cref="SyntaxEditor"/> at the specified offset.</returns>
		private string GetQuickInfo(SyntaxEditor syntaxEditor, ref int offset) {
			// Get the identifier at the offset, if any
			TextStream stream = syntaxEditor.Document.GetTextStream(offset);
			if (!stream.IsAtTokenStart)
				stream.GoToCurrentTokenStart();
			offset = stream.Offset;
			if (stream.Token.ID != SimpleTokenID.Identifier)
				return null;
			string text = stream.TokenText;
			
			// Get the containing node
			CompilationUnit compilationUnit = syntaxEditor.Document.SemanticParseData as CompilationUnit;
			AstNode containingNode = null;
			if (compilationUnit != null)
				containingNode = compilationUnit.FindNodeRecursive(stream.Offset) as AstNode;

			if (containingNode != null) {
				// Get the containing function
				FunctionDeclaration containingFunctionDeclaration = containingNode.FindAncestor(typeof(FunctionDeclaration)) as FunctionDeclaration;
				if ((containingFunctionDeclaration != null) && (!containingFunctionDeclaration.BlockStatement.Contains(stream.Offset)))
					containingFunctionDeclaration = null;
				
				// Check for a function parameter
				if (containingFunctionDeclaration != null) {
					foreach (Identifier parameterName in containingFunctionDeclaration.Parameters) {
						if (parameterName.Text == text)
							return this.GetQuickInfoForFunctionParameter(syntaxEditor, parameterName, true);
					}		
				}

				// Check for a function name
				foreach (FunctionDeclaration functionDeclaration in compilationUnit.Functions) {
					if (functionDeclaration.Name.Text == text)
						return this.GetQuickInfoForFunctionDeclaration(syntaxEditor, functionDeclaration, -1, true);
				}		
			}

			return null;
		}

		/// <summary>
		/// Gets the formatted text to display in quick info for the specified <see cref="FunctionDeclaration"/>.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that is requesting the quick info.</param>
		/// <param name="functionDeclaration">The <see cref="FunctionDeclaration"/> to examine.</param>
		/// <param name="parameterIndex">The index of the selected parameter.</param>
		/// <param name="reflectionIconsEnabled">Whether reflection icons are enabled.</param>
		/// <returns>The formatted text to display in quick info for the specified <see cref="FunctionDeclaration"/>.</returns>
		private string GetQuickInfoForFunctionDeclaration(SyntaxEditor syntaxEditor, FunctionDeclaration functionDeclaration, int parameterIndex, bool reflectionIconsEnabled) {
			StringBuilder result = new StringBuilder();
			if (reflectionIconsEnabled)
				result.Append("<img src=\"resource:PublicMethod\" align=\"absbottom\"/> ");
			result.Append("<span style=\"color: blue;\">function</span> ");
			result.Append(String.Format("<span style=\"color: teal;\">{0}</span>", IntelliPrompt.EscapeMarkupText(functionDeclaration.Name.Text)));
			result.Append("(");
			IAstNodeList parameters = functionDeclaration.Parameters;
			for (int index = 0; index < parameters.Count; index++) {
				if (index > 0)
					result.Append(", " );
				if (parameterIndex == index)
					result.Append("<b>");
				result.Append(IntelliPrompt.EscapeMarkupText(((Identifier)parameters[index]).Text));
				if (parameterIndex == index)
					result.Append("</b>");
			}
			result.Append(")<br/>");
			result.Append(String.Format("<span style=\"color: green;\">This function has {0} parameter(s).</span>", parameters.Count));
			return result.ToString();
		}
		
		/// <summary>
		/// Gets the formatted text to display in quick info for the specified function parameter.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that is requesting the quick info.</param>
		/// <param name="parameterName">The <see cref="Identifier"/> to examine.</param>
		/// <param name="reflectionIconsEnabled">Whether reflection icons are enabled.</param>
		/// <returns>The formatted text to display in quick info for the specified function parameter.</returns>
		private string GetQuickInfoForFunctionParameter(SyntaxEditor syntaxEditor, Identifier parameterName, bool reflectionIconsEnabled) {
			return String.Format("<i style=\"color: gray;\">(parameter)</i> {0}<b>{1}</b><br/><span style=\"color: green;\">Parameter is of function {2}.</span>", 
				(reflectionIconsEnabled ? @"<img src=""resource:PublicField"" align=""absbottom""/> " : String.Empty),
				IntelliPrompt.EscapeMarkupText(parameterName.Text),
				IntelliPrompt.EscapeMarkupText(((FunctionDeclaration)parameterName.ParentNode).Name.Text)
				);
		}

		/// <summary>
		/// Gets the formatted text to display in quick info for the specified keyword.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that is requesting the quick info.</param>
		/// <param name="keyword">The keyword to examine.</param>
		/// <returns>The formatted text to display in quick info for the specified keyword.</returns>
		private string GetQuickInfoForKeyword(SyntaxEditor syntaxEditor, string keyword) {
			return String.Format("<i style=\"color: gray;\">(keyword)</i> <b>{0}</b>",  IntelliPrompt.EscapeMarkupText(keyword));
		}
		
		/// <summary>
		/// Provides the core functionality to show an IntelliPrompt member list based on the current context in a <see cref="SyntaxEditor"/>.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt member list.</param>
		/// <param name="completeWord">Whether to complete the word.</param>
		/// <returns>
		/// <c>true</c> if an auto-complete occurred or if an IntelliPrompt member list is displayed; otherwise, <c>false</c>.
		/// </returns>
		private bool ShowIntelliPromptMemberList(SyntaxEditor syntaxEditor, bool completeWord) {
			// Try and ensure the compilation unit is up-to-date
			SemanticParserService.WaitForParse(SemanticParserServiceRequest.GetParseHashKey(syntaxEditor.Document, syntaxEditor.Document));

			// Initialize the member list
			IntelliPromptMemberList memberList = syntaxEditor.IntelliPrompt.MemberList;
			memberList.ResetAllowedCharacters();
			memberList.Clear();
			memberList.ImageList = SyntaxEditor.ReflectionImageList;

			// Get the target text range
			TextRange targetTextRange = TextRange.Deleted;
			TextStream stream = syntaxEditor.Document.GetTextStream(syntaxEditor.Caret.Offset);
			if (stream.IsAtTokenStart) {
				if (stream.Offset > 0) {
					stream.GoToPreviousToken();
					if (stream.Token.ID == SimpleTokenID.Identifier)
						targetTextRange = stream.Token.TextRange;
				}
			}
			else {
				stream.GoToCurrentTokenStart();
				targetTextRange = stream.Token.TextRange;
			}			

			// Get the containing node
			CompilationUnit compilationUnit = syntaxEditor.Document.SemanticParseData as CompilationUnit;
			AstNode containingNode = null;
			if (compilationUnit != null)
				containingNode = compilationUnit.FindNodeRecursive(stream.Offset) as AstNode;

			// Get the member list items
			Hashtable memberListItemHashtable = new Hashtable();

			// Add keywords
			if (containingNode == null)
				memberListItemHashtable["function"] = new IntelliPromptMemberListItem("function", (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword);
			else {
				memberListItemHashtable["return"] = new IntelliPromptMemberListItem("return", (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword);
				memberListItemHashtable["var"] = new IntelliPromptMemberListItem("var", (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword);
			}

			// Add functions 
			if ((compilationUnit != null) && (containingNode != null)) {
				foreach (FunctionDeclaration functionDeclaration in compilationUnit.Functions)
					memberListItemHashtable[functionDeclaration.Name.Text] = new IntelliPromptMemberListItem(functionDeclaration.Name.Text, 
						(int)ActiproSoftware.Products.SyntaxEditor.IconResource.PublicMethod, functionDeclaration);
			}

			if (memberListItemHashtable.Count > 0) {
				IntelliPromptMemberListItem[] items = new IntelliPromptMemberListItem[memberListItemHashtable.Count];
				memberListItemHashtable.Values.CopyTo(items, 0);
				memberList.AddRange(items);
			}

			// Show the list
			if (memberList.Count > 0) {
				if (targetTextRange.IsDeleted)
					memberList.Show();
				else if (completeWord)
					memberList.CompleteWord(targetTextRange.StartOffset, targetTextRange.Length);
				else
					memberList.Show(targetTextRange.StartOffset, targetTextRange.Length);
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Provides the core functionality to shows parameter info based on the current context in a <see cref="SyntaxEditor"/>.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the parameter info.</param>
		/// <param name="offset">The offset to examine.</param>
		/// <returns>
		/// <c>true</c> if parameter info is displayed; otherwise, <c>false</c>.
		/// </returns>
		private bool ShowParameterInfoCore(SyntaxEditor syntaxEditor, int offset) {
			// Initialize the parameter info
			syntaxEditor.IntelliPrompt.ParameterInfo.Hide();
			syntaxEditor.IntelliPrompt.ParameterInfo.Info.Clear();
			syntaxEditor.IntelliPrompt.ParameterInfo.SelectedIndex = 0;
			
			// Get the identifier at the offset, if any
			TextStream stream = syntaxEditor.Document.GetTextStream(offset);
			if (!stream.IsAtTokenStart)
				stream.GoToCurrentTokenStart();
			stream.GoToPreviousToken();
			if (stream.Token.ID != SimpleTokenID.OpenParenthesis)
				return false;
			stream.GoToPreviousToken();
			if (stream.Token.ID != SimpleTokenID.Identifier)
				return false;
			string text = stream.TokenText;

			// Get the containing node
			CompilationUnit compilationUnit = syntaxEditor.Document.SemanticParseData as CompilationUnit;
			AstNode containingNode = null;
			if (compilationUnit != null)
				containingNode = compilationUnit.FindNodeRecursive(stream.Offset) as AstNode;

			if (containingNode != null) {
				// Get the containing function
				FunctionDeclaration containingFunctionDeclaration = containingNode.FindAncestor(typeof(FunctionDeclaration)) as FunctionDeclaration;
				if ((containingFunctionDeclaration != null) && (!containingFunctionDeclaration.BlockStatement.Contains(stream.Offset)))
					containingFunctionDeclaration = null;
				
				if (containingFunctionDeclaration != null) {
					foreach (FunctionDeclaration functionDeclaration in compilationUnit.Functions) {
						if (functionDeclaration.Name.Text == text) {
							IAstNodeList parameters = functionDeclaration.Parameters;
							if (parameters.Count > 0) {
								// Determine the parameter text range
								TextRange parameterTextRange = new TextRange(offset);
								stream.Offset = offset - 1;
								if (stream.GoToNextMatchingToken(stream.Token))
									parameterTextRange = new TextRange(offset, stream.Offset + 1);
								else {
									int documentLineIndex = syntaxEditor.Document.Lines.IndexOf(offset);
									parameterTextRange = new TextRange(offset, Math.Max(offset, syntaxEditor.Document.Lines[documentLineIndex].EndOffset) + 1);
								}

								// Configure the parameter info
								syntaxEditor.IntelliPrompt.ParameterInfo.ValidTextRange = parameterTextRange;
								syntaxEditor.IntelliPrompt.ParameterInfo.CloseDelimiterCharacter = ')';

								// Update the parameter index
								syntaxEditor.IntelliPrompt.ParameterInfo.UpdateParameterIndex();

								// Add an item
								syntaxEditor.IntelliPrompt.ParameterInfo.Info.Add(this.GetQuickInfoForFunctionDeclaration(
									syntaxEditor, functionDeclaration, syntaxEditor.IntelliPrompt.ParameterInfo.ParameterIndex, true));

								// Store the function declaration in the context
								syntaxEditor.IntelliPrompt.ParameterInfo.Context = functionDeclaration;

								// Show the parameter info
								syntaxEditor.IntelliPrompt.ParameterInfo.Show(offset);
								return true;
							}
							break;
						}
					}
				}
			}

			return false;
		}
				
		/// <summary>
		/// Updates the parameter info's selected item's text.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> to examine.</param>
		/// <param name="force">Whether to force the update.  Otherwise the update only occurs if the text is not yet initialized.</param>
		/// <returns>
		/// <c>true</c> if an update occurred; otherwise, <c>false</c>.
		/// </returns>
		private bool UpdateParameterInfoSelectedText(SyntaxEditor syntaxEditor, bool force) {
			// If the parameter info is visible and there is a context available...
			if ((syntaxEditor.IntelliPrompt.ParameterInfo.Visible) && (syntaxEditor.IntelliPrompt.ParameterInfo.Context is FunctionDeclaration)) {
				// Get the function declaration
				FunctionDeclaration functionDeclaration = (FunctionDeclaration)syntaxEditor.IntelliPrompt.ParameterInfo.Context;

				// If the parameter info item needs markup...
				if ((force) || (syntaxEditor.IntelliPrompt.ParameterInfo.Info[syntaxEditor.IntelliPrompt.ParameterInfo.SelectedIndex] == null)) {
					// Update the parameter info item
					syntaxEditor.IntelliPrompt.ParameterInfo.Info[syntaxEditor.IntelliPrompt.ParameterInfo.SelectedIndex] = 
						this.GetQuickInfoForFunctionDeclaration(syntaxEditor, functionDeclaration, syntaxEditor.IntelliPrompt.ParameterInfo.ParameterIndex, true);
					return true;
				}
			}
			return false;
		}
	
		/////////////////////////////////////////////////////////////////////////////////////////////////////
		// PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////
	
		/// <summary>
		/// Resets the <see cref="AutomaticOutliningBehavior"/> property to its default value.
		/// </summary>
		public override void ResetAutomaticOutliningBehavior() {
			this.AutomaticOutliningBehavior = ActiproSoftware.SyntaxEditor.AutomaticOutliningBehavior.SemanticParseDataChange;
		}
		/// <summary>
		/// Indicates whether the <see cref="AutomaticOutliningBehavior"/> property should be persisted.
		/// </summary>
		/// <returns>
		/// <c>true</c> if the property value has changed from its default; otherwise, <c>false</c>.
		/// </returns>
		public override bool ShouldSerializeAutomaticOutliningBehavior() {
			return (this.AutomaticOutliningBehavior != ActiproSoftware.SyntaxEditor.AutomaticOutliningBehavior.SemanticParseDataChange);
		}
		
		/// <summary>
		/// Creates an <see cref="IToken"/> that represents the end of a document.
		/// </summary>
		/// <param name="startOffset">The start offset of the <see cref="IToken"/>.</param>
		/// <param name="lexicalState">The <see cref="ILexicalState"/> that contains the token.</param>
		/// <returns>An <see cref="IToken"/> that represents the end of a document.</returns>
		public override IToken CreateDocumentEndToken(int startOffset, ILexicalState lexicalState) {
			return new SimpleToken(startOffset, 0, LexicalParseFlags.None, null, new LexicalStateAndIDTokenLexicalParseData(lexicalState, (byte)SimpleTokenID.DocumentEnd));
		}

		/// <summary>
		/// Creates an <see cref="IToken"/> that represents an invalid range of text.
		/// </summary>
		/// <param name="startOffset">The start offset of the <see cref="IToken"/>.</param>
		/// <param name="length">The length of the <see cref="IToken"/>.</param>
		/// <param name="lexicalState">The <see cref="ILexicalState"/> that contains the token.</param>
		/// <returns>An <see cref="IToken"/> that represents an invalid range of text.</returns>
		public override IToken CreateInvalidToken(int startOffset, int length, ILexicalState lexicalState) {
			return new SimpleToken(startOffset, length, LexicalParseFlags.None, null, new LexicalStateAndIDTokenLexicalParseData(lexicalState, (byte)SimpleTokenID.Invalid));
		}
		
		/// <summary>
		/// Creates an <see cref="IToken"/> that represents the range of text with the specified lexical parse data.
		/// </summary>
		/// <param name="startOffset">The start offset of the token.</param>
		/// <param name="length">The length of the token.</param>
		/// <param name="lexicalParseFlags">The <see cref="LexicalParseFlags"/> for the token.</param>
		/// <param name="parentToken">The <see cref="IToken"/> that starts the current state scope specified by the <see cref="ITokenLexicalParseData.LexicalState"/> property.</param>
		/// <param name="lexicalParseData">The <see cref="ITokenLexicalParseData"/> that contains lexical parse information about the token.</param>
		/// <returns></returns>
		public override IToken CreateToken(int startOffset, int length, LexicalParseFlags lexicalParseFlags, IToken parentToken, ITokenLexicalParseData lexicalParseData) {
			return new SimpleToken(startOffset, length, lexicalParseFlags, parentToken, lexicalParseData);
		}
		
		/// <summary>
		/// Resets the <see cref="ErrorDisplayEnabled"/> property to its default value.
		/// </summary>
		public override void ResetErrorDisplayEnabled() {
			this.ErrorDisplayEnabled = true;
		}
		/// <summary>
		/// Indicates whether the <see cref="ErrorDisplayEnabled"/> property should be persisted.
		/// </summary>
		/// <returns>
		/// <c>true</c> if the property value has changed from its default; otherwise, <c>false</c>.
		/// </returns>
		public override bool ShouldSerializeErrorDisplayEnabled() {
			return !this.ErrorDisplayEnabled;
		}

		/// <summary>
		/// Returns the <see cref="HighlightingStyle"/> for the specified <see cref="IToken"/>.
		/// </summary>
		/// <param name="token">The <see cref="IToken"/> to examine.</param>
		/// <returns>The <see cref="HighlightingStyle"/> for the specified <see cref="IToken"/>.</returns>
		public override HighlightingStyle GetHighlightingStyle(IToken token) {
			switch (token.ID) {
				case SimpleTokenID.SingleLineComment:
				case SimpleTokenID.MultiLineComment:
					return this.HighlightingStyles["CommentStyle"];
				case SimpleTokenID.Number:
					return this.HighlightingStyles["NumberStyle"];
				default:
					if ((token.ID > SimpleTokenID.KeywordStart) && (token.ID < SimpleTokenID.KeywordEnd))
						return this.HighlightingStyles["KeywordStyle"];
					else
						return token.LexicalState.DefaultHighlightingStyle;
			}
		}
			
		/// <summary>
		/// Gets the token string representation for the specified token ID.
		/// </summary>
		/// <param name="tokenID">The ID of the token to examine.</param>
		/// <returns>The token string representation for the specified token ID.</returns>
		public override string GetTokenString(int tokenID) {
			if ((tokenID > (int)SimpleTokenID.KeywordStart) && (tokenID < (int)SimpleTokenID.KeywordEnd))
				return SimpleTokenID.GetTokenKey(tokenID).ToLower();

			switch (tokenID) {
				// General
				case SimpleTokenID.DocumentEnd:
					return "Document end";
				case SimpleTokenID.Whitespace:
					return "Whitespace";
				case SimpleTokenID.LineTerminator:
					return "Line terminator";
				case SimpleTokenID.SingleLineComment:
					return "Single-line comment";
				case SimpleTokenID.MultiLineComment:
					return "Multi-line comment";
				case SimpleTokenID.Number:
					return "Number";
				case SimpleTokenID.Identifier:
					return "Identifier";
				// Operators
				case SimpleTokenID.OpenCurlyBrace:
					return "{";
				case SimpleTokenID.CloseCurlyBrace:
					return "}";
				case SimpleTokenID.OpenParenthesis:
					return "(";
				case SimpleTokenID.CloseParenthesis:
					return ")";
				case SimpleTokenID.Comma:
					return ",";
				case SimpleTokenID.SemiColon:
					return ";";
				case SimpleTokenID.Addition:
					return "+";
				case SimpleTokenID.Subtraction:
					return "-";
				case SimpleTokenID.Multiplication:
					return "*";
				case SimpleTokenID.Division:
					return "/";
				case SimpleTokenID.Assignment:
					return "=";
				case SimpleTokenID.Equality:
					return "==";
				case SimpleTokenID.Inequality:
					return "!=";
			}

			return null;
		}
		
		/// <summary>
		/// Performs an auto-complete if the <see cref="SyntaxEditor"/> context with which the IntelliPrompt member list is initialized causes a single selection.
		/// Otherwise, displays a member list in the <see cref="SyntaxEditor"/>.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt member list.</param>
		/// <returns>
		/// <c>true</c> if an auto-complete occurred or if an IntelliPrompt member list is displayed; otherwise, <c>false</c>.
		/// </returns>
		public override bool IntelliPromptCompleteWord(SyntaxEditor syntaxEditor) {
			return this.ShowIntelliPromptMemberList(syntaxEditor, true);
		}
		
		/// <summary>
		/// Gets whether IntelliPrompt member list features are supported by the language.
		/// </summary>
		/// <value>
		/// <c>true</c> if IntelliPrompt member list features are supported by the language; otherwise, <c>false</c>.
		/// </value>
		/// <remarks>
		/// If this property is <c>true</c>, then the <see cref="IntelliPromptCompleteWord"/> and <see cref="ShowIntelliPromptMemberList"/> methods may be used.
		/// </remarks>
		public override bool IntelliPromptMemberListSupported {
			get {
				return true;
			}
		}
			
		/// <summary>
		/// Gets whether IntelliPrompt parameter info features are supported by the language.
		/// </summary>
		/// <value>
		/// <c>true</c> if IntelliPrompt parameter info features are supported by the language; otherwise, <c>false</c>.
		/// </value>
		/// <remarks>
		/// If this property is <c>true</c>, then the <see cref="ShowIntelliPromptParameterInfo"/> method may be used.
		/// </remarks>
		public override bool IntelliPromptParameterInfoSupported {
			get {
				return true;
			}
		}

		/// <summary>
		/// Gets whether IntelliPrompt quick info features are supported by the language.
		/// </summary>
		/// <value>
		/// <c>true</c> if IntelliPrompt quick info features are supported by the language; otherwise, <c>false</c>.
		/// </value>
		/// <remarks>
		/// If this property is <c>true</c>, then the <see cref="ShowIntelliPromptQuickInfo"/> method may be used.
		/// </remarks>
		public override bool IntelliPromptQuickInfoSupported {
			get {
				return true;
			}
		}

		/// <summary>
		/// Resets the <see cref="SyntaxLanguage.LineCommentDelimiter"/> property to its default value.
		/// </summary>
		public override void ResetLineCommentDelimiter() {
			this.LineCommentDelimiter = "//";
		}
		/// <summary>
		/// Indicates whether the <see cref="SyntaxLanguage.LineCommentDelimiter"/> property should be persisted.
		/// </summary>
		/// <returns>
		/// <c>true</c> if the property value has changed from its default; otherwise, <c>false</c>.
		/// </returns>
		public override bool ShouldSerializeLineCommentDelimiter() {
			return (this.LineCommentDelimiter != "//");
		}
		
		/// <summary>
		/// Gets the <see cref="IMergableLexicalParser"/> that can be used for lexical parsing of the language.
		/// </summary>
		/// <value>The <see cref="IMergableLexicalParser"/> that can be used for lexical parsing of the language.</value>
		protected override IMergableLexicalParser MergableLexicalParser {
			get {
				return lexicalParser;
			}
		}				
		
		/// <summary>
		/// Occurs when a description tip is about to be displayed for the selected IntelliPrompt member list item,
		/// but the item has no description set.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
		/// <param name="e">An <c>EventArgs</c> that contains the event data.</param>
		protected override void OnSyntaxEditorIntelliPromptMemberListItemDescriptionRequested(SyntaxEditor syntaxEditor, EventArgs e) {
			// Get the selected item
			IntelliPromptMemberListItem item = syntaxEditor.IntelliPrompt.MemberList.SelectedItem;
			if (item == null)
				return;

			// Update the item description
			if (item.Tag is FunctionDeclaration)
				item.Description = this.GetQuickInfoForFunctionDeclaration(syntaxEditor, (FunctionDeclaration)item.Tag, -1, false);
			else if (item.Tag is Identifier) {
				Identifier identifier = (Identifier)item.Tag;
				if ((identifier.ParentNode is FunctionDeclaration) && (identifier.ContextID == FunctionDeclaration.ParameterContextID))
					item.Description = this.GetQuickInfoForFunctionParameter(syntaxEditor, identifier, false);
			}
			else if (item.ImageIndex == (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword)
				item.Description = this.GetQuickInfoForKeyword(syntaxEditor, item.Text);
		}
		
		/// <summary>
		/// Occurs after the parameter index of the IntelliPrompt parameter info is changed while the parameter info is visible.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
		/// <param name="e">An <c>EventArgs</c> that contains the event data.</param>
		protected override void OnSyntaxEditorIntelliPromptParameterInfoParameterIndexChanged(SyntaxEditor syntaxEditor, EventArgs e) {
			if (this.UpdateParameterInfoSelectedText(syntaxEditor, true))
				syntaxEditor.IntelliPrompt.ParameterInfo.MeasureAndResize(syntaxEditor.IntelliPrompt.ParameterInfo.Bounds.Location);
		}

		/// <summary>
		/// Occurs before a <see cref="SyntaxEditor.KeyTyped"/> event is raised 
		/// for a <see cref="SyntaxEditor"/> that has a <see cref="Document"/> using this language.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
		/// <param name="e">An <c>KeyTypedEventArgs</c> that contains the event data.</param>
		protected override void OnSyntaxEditorKeyTyped(SyntaxEditor syntaxEditor, KeyTypedEventArgs e) {
			switch (e.KeyChar) {
				case '(':
					if (syntaxEditor.Caret.Offset > 1) {
						// Show the parameter info for code
						this.ShowParameterInfoCore(syntaxEditor, syntaxEditor.Caret.Offset);
					}
					break;
				case ')':
					if (syntaxEditor.Caret.Offset > 1) {
						// Show the parameter info for the parent context level if there is one
						this.ShowIntelliPromptParameterInfo(syntaxEditor);
					}
					break;
				case ',':
					if ((!syntaxEditor.IntelliPrompt.ParameterInfo.Visible) && (syntaxEditor.Caret.Offset > 1) && (syntaxEditor.SelectedView.GetCurrentToken().LexicalState == this.LexicalStates["DefaultState"])) {
						// Show the parameter info for the context level if parameter info is not already displayed
						this.ShowIntelliPromptParameterInfo(syntaxEditor);
					}
					break;
			}
		}

		/// <summary>
		/// Occurs when the mouse is hovered over an <see cref="EditorView"/>.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
		/// <param name="e">An <c>EditorViewMouseEventArgs</c> that contains the event data.</param>
		protected override void OnSyntaxEditorViewMouseHover(SyntaxEditor syntaxEditor, EditorViewMouseEventArgs e) {
			if ((e.HitTestResult.Token == null) || (e.ToolTipText != null))
				return;

			int offset = e.HitTestResult.Token.StartOffset;
			e.ToolTipText = this.GetQuickInfo(syntaxEditor, ref offset);
		}
		
		/// <summary>
		/// Performs automatic outlining over the specified <see cref="TextRange"/> of the <see cref="Document"/>.
		/// </summary>
		/// <param name="document">The <see cref="Document"/> to examine.</param>
		/// <param name="parseTextRange">A <see cref="TextRange"/> indicating the offset range to parse.</param>
		/// <returns>A <see cref="TextRange"/> containing the offset range that was modified by outlining.</returns>
		public override TextRange PerformAutomaticOutlining(Document document, TextRange parseTextRange) {
			// If there is another pending semantic parser request (probably due to typing), assume that the existing outlining structure 
			//   in the document is more up-to-date and wait until the final request comes through before updating the outlining again
			if (!SemanticParserService.HasPendingRequest(SemanticParserServiceRequest.GetParseHashKey(document, document)))
				return new CollapsibleNodeOutliningParser().UpdateOutlining(document, parseTextRange, document.SemanticParseData as CompilationUnit);
			else
				return TextRange.Deleted;
		}
		
		/// <summary>
		/// Semantically parses the specified <see cref="TextRange"/> of the <see cref="Document"/>.
		/// </summary>
		/// <param name="document">The <see cref="Document"/> to examine.</param>
		/// <param name="parseTextRange">A <see cref="TextRange"/> indicating the offset range to parse.</param>
        /// <param name="flags">A <see cref="SemanticParseFlags"/> that indicates semantic parse flags.</param>
		public override void PerformSemanticParse(Document document, TextRange parseTextRange, SemanticParseFlags flags) {
			SemanticParserService.Parse(new SemanticParserServiceRequest(SemanticParserServiceRequest.MediumPriority,
				document, parseTextRange, flags, this, document));
		}
		
		/// <summary>
		/// Semantically parses the text in the <see cref="MergableLexicalParserManager"/>.
		/// </summary>
		/// <param name="manager">The <see cref="MergableLexicalParserManager"/> that is managing the mergable language and the text to parse.</param>
		/// <returns>An object that contains the results of the semantic parsing operation.</returns>
		protected override object PerformSemanticParse(MergableLexicalParserManager manager) {
			SimpleRecursiveDescentLexicalParser lexicalParser = new SimpleRecursiveDescentLexicalParser(this, manager);
			lexicalParser.InitializeTokens();
			SimpleSemanticParser semanticParser = new SimpleSemanticParser(lexicalParser);
			semanticParser.Parse();
			return semanticParser.CompilationUnit;
		}
		
		/// <summary>
		/// Displays an IntelliPrompt member list in a <see cref="SyntaxEditor"/> based on the current context.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt member list.</param>
		/// <returns>
		/// <c>true</c> if an IntelliPrompt member list is displayed; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// Only call this method if the <see cref="IntelliPromptMemberListSupported"/> property is set to <c>true</c>.
		/// </remarks>
		public override bool ShowIntelliPromptMemberList(SyntaxEditor syntaxEditor) {
			return this.ShowIntelliPromptMemberList(syntaxEditor, false);
		}
		
		/// <summary>
		/// Displays IntelliPrompt parameter info in a <see cref="SyntaxEditor"/> based on the current context.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt parameter info.</param>
		/// <returns>
		/// <c>true</c> if IntelliPrompt parameter info is displayed; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// Only call this method if the <see cref="IntelliPromptParameterInfoSupported"/> property is set to <c>true</c>.
		/// </remarks>
		public override bool ShowIntelliPromptParameterInfo(SyntaxEditor syntaxEditor) {
			// Determine the maximum number of characters to look back at
			int minOffset = Math.Max(0, syntaxEditor.Caret.Offset - 500);

			// Try and find an open '('
			int targetOffset = -1;
			TextStream stream = syntaxEditor.Document.GetTextStream(syntaxEditor.Caret.Offset);
			stream.GoToCurrentTokenStart();
			bool exitLoop = false;
			while (stream.Offset > minOffset) {
				IToken token = stream.ReadTokenReverse();
				switch (token.ID) {
					case SimpleTokenID.CloseCurlyBrace:
					case SimpleTokenID.CloseParenthesis:
						stream.GoToPreviousMatchingToken(token);
						break;
					case SimpleTokenID.OpenParenthesis:
						targetOffset = token.EndOffset;
						exitLoop = true;
						break;
				}
				if (exitLoop)
					break;
			}

			if (targetOffset != -1)
				return this.ShowParameterInfoCore(syntaxEditor, targetOffset);
			else
				return false;
		}
		
		/// <summary>
		/// Displays IntelliPrompt quick info in a <see cref="SyntaxEditor"/> based on the current context.
		/// </summary>
		/// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt quick info.</param>
		/// <returns>
		/// <c>true</c> if IntelliPrompt quick info is displayed; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// Only call this method if the <see cref="IntelliPromptQuickInfoSupported"/> property is set to <c>true</c>.
		/// </remarks>
		public override bool ShowIntelliPromptQuickInfo(SyntaxEditor syntaxEditor) {
			int offset = syntaxEditor.Caret.Offset;

			// Get the info for the context at the caret
			string quickInfo = this.GetQuickInfo(syntaxEditor, ref offset);

			// No info was found... try the offset right before the caret
			if (offset > 0) {
				offset = syntaxEditor.Caret.Offset - 1;
				quickInfo = this.GetQuickInfo(syntaxEditor, ref offset);
			}

			// Show the quick info if there is any
			if (quickInfo != null) {
				syntaxEditor.IntelliPrompt.QuickInfo.Show(offset, quickInfo);
				return true;
			}

			return false;
		}
		
	}

}
